﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.Infra.Core.ProcessManager.Server;
using System.Threading;
using PSE.Framework.Communication.Client.File.FlatFile;
using PSE.Framework.Communication.Client.File.XmlFile;
using PSE.Framework.Communication.Adapters.File;
using PSE.Framework.Communication.Client.File;
using PSE.ProcessManager.Server.Process.Security;
using Sinacor.Infra.Core.ProcessManager.Server.Security;
using Sinacor.Infra.Service.Validation;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using PSE.ProcessManager.Common.Process;

namespace Sinacor.Process.Bovespa.Distribuicao.Server.ProcessLogic
{
    public class Importacao : BaseProcessLogic
    {
        #region Atributos

        private ManualResetEvent _importFinishControl;
        private AsyncFileInputClient<object> _client = null;
        private List<ProcessEntity.DistribuicaoDetail> _distribuicoes = null;
        private ProcessEntity.DistribuicaoHeader _header = null;
        private int _erroCodigoCorretoraDivergente = 109855;
        private int _erroArquivoInvalido = 109858;
        private int _erroMovimentoFechado = 109854;
        private Translator.LayoutToProcessEntity.Distribuicao _translatorDistribuicao;
        private int _erroNenhumaLinhaProcessar = 109857;
        private string _filePath;
        private bool _erroLeituraArquivo;

        #endregion

        #region Eventos

        public event EventHandler ImportacaoIniciada;
        public event EventHandler<ProcessEventArgs<ProcessEntity.Parameter.FiltroRetornoImportacaoDistribuicao>> ImportacaoConcluida;

        #endregion

        #region Construtor

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public Importacao()
        {
            _translatorDistribuicao = new Translator.LayoutToProcessEntity.Distribuicao();
        }

        #endregion

        #region Propriedades

        public int ErroCodigoCorretoraDivergente
        {
            get { return _erroCodigoCorretoraDivergente; }
            set { _erroCodigoCorretoraDivergente = value; }
        }

        public int ErroMovimentoFechado
        {
            get { return _erroMovimentoFechado; }
            set { _erroMovimentoFechado = value; }
        }

        public int ErroNenhumaLinhaProcessar
        {
            get { return _erroNenhumaLinhaProcessar; }
            set { _erroNenhumaLinhaProcessar = value; }
        }

        public int ErroArquivoInvalido
        {
            get { return _erroArquivoInvalido; }
            set { _erroArquivoInvalido = value; }
        }

        public bool ErroLeituraArquivo
        {
            get { return _erroLeituraArquivo; }
            set { _erroLeituraArquivo = value; }
        }

        #endregion

        #region EventHandler

        private void _client_RecordReadError(object sender, RecordReadErrorEventArgs e)
        {
            _erroLeituraArquivo = true;

            this.HandleException(new BusinessException(ErroArquivoInvalido, e.Exception), true);

            FecharClient();
        }

        private void _client_RecordRead(object sender, RecordEventArgs e)
        {
            Layout.DistribuicaoXMLIdentificador xmlNEGS;

            try
            {
                if (e.Record is Layout.DistribuicaoHeader)
                {
                    _header = _translatorDistribuicao.ToDistribuicaoHeader((Layout.DistribuicaoHeader)e.Record);
                }
                else if (e.Record is Layout.DistribuicaoDetail)
                {
                    _distribuicoes.Add(_translatorDistribuicao.ToDistribuicaoDetail((Layout.DistribuicaoDetail)e.Record));
                }
                else if (e.Record is Layout.DistribuicaoXMLIdentificador)
                {
                    xmlNEGS = (Layout.DistribuicaoXMLIdentificador)e.Record;

                    _header = _translatorDistribuicao.ToDistribuicaoHeader(xmlNEGS);

                    if (xmlNEGS.Evento != null
                        && xmlNEGS.Evento.Negocio != null)
                        _distribuicoes.AddRange(TraduzirDistribuicaoXML(xmlNEGS.Evento.Negocio, sender));
                }
            }
            catch (Exception ex)
            {
                this.HandleException(ex, true);

                FecharClient();
            }
        }

        private void _client_FileClosed(object sender, FileClosedEventArgs e)
        {
            FecharClient();
        }

        private void _client_FileCanceled(object sender, FileCanceledEventArgs e)
        {
            FecharClient();
        }

        private void _client_FileOpened(object sender, FileOpenedEventArgs e)
        {
            if (!IsAborted)
            {
                _erroLeituraArquivo = false;

                _header = null;

                if (_distribuicoes == null)
                    _distribuicoes = new List<ProcessEntity.DistribuicaoDetail>();

                _distribuicoes.Clear();

                _filePath = e.FilePath;
            }
        }

        #endregion

        #region Métodos Públicos

        public void ImportarDistribuicao(ProcessEntity.Parameter.FiltroImportacaoDistribuicao entity)
        {
            ResourceAccess.Importacao raImportacao;
            ResourceAccess.ImportacaoExportacao raImportacaoExportacao;
            int codigoEmpresaBolsa;
            Communication.ControleMovimentoOrdens communicatonControleMovimentoOrdens;
            ProcessEntity.InformacoesMovimento informacaoMovimento;
            SinacorValidation validation = null;
            decimal porcentagem = 5;
            decimal porcentagemItem;
            int atualizarStatus;
            List<ProcessEntity.Broker> brokers;
            ProcessEntity.Parameter.FiltroRetornoImportacaoDistribuicao filtroRetorno;

            try
            {
                if (ImportacaoIniciada != null)
                {
                    ImportacaoIniciada(this, new EventArgs());
                }

                if (!IsAborted)
                {
                    validation = new SinacorValidation();
                    validation.Validate<ProcessEntity.Parameter.FiltroImportacaoDistribuicao>(entity);

                    AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.LendoArquivo");

                    LerArquivoNegocio(entity);

                    if (!ErroLeituraArquivo)
                    {
                        if (_distribuicoes.Count == 0)
                        {
                            throw new ValidationException(ErroNenhumaLinhaProcessar);
                        }

                        if (!IsAborted)
                        {
                            porcentagem = 10;
                            AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.ValidandoArquivo");

                            if (_header.NomeArquivo != "TORDIST"
                                && !IsAborted)
                                throw new ValidationException(ErroArquivoInvalido);

                            communicatonControleMovimentoOrdens = new Communication.ControleMovimentoOrdens();
                            informacaoMovimento = communicatonControleMovimentoOrdens.ObterInformacoesMovimento();

                            if (informacaoMovimento.EstadoMovimento == ProcessEntity.Enum.TipoMovimento.Fechado
                                & !IsAborted)
                                throw new BusinessException(ErroMovimentoFechado);

                            raImportacao = new ResourceAccess.Importacao();
                            raImportacaoExportacao = new ResourceAccess.ImportacaoExportacao();

                            codigoEmpresaBolsa = raImportacaoExportacao.ObterCodigoEmpresaBolsa();

                            if (_header.CodigoUsuario != codigoEmpresaBolsa
                                && !IsAborted)
                            {
                                throw new BusinessException(ErroCodigoCorretoraDivergente);
                            }

                            if (!IsAborted)
                            {
                                raImportacao.LimparTabelaImportacao();

                                if (!IsAborted)
                                {
                                    porcentagemItem = (_distribuicoes.Count > 0) ? 70M / Convert.ToDecimal(_distribuicoes.Count) : 70;

                                    atualizarStatus = (_distribuicoes.Count < 100) ? 10 : 100;

                                    BeginTransaction(AuthenticationContext<SinacorAuthenticationData>.Current.AuthenticationData.CompanyId);

                                    for (int i = 0; i < _distribuicoes.Count; i++)
                                    {
                                        if (IsAborted)
                                            break;

                                        if (!entity.CalcularAcrescimoDecrescimo)
                                        {
                                            _distribuicoes[i].PercentualAcrescimo = null;
                                        }

                                        porcentagem += porcentagemItem;
                                        raImportacao.ImportarDistribuicao(_distribuicoes[i]);

                                        if (i % atualizarStatus == 0)
                                        {
                                            AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.ImportandoDistribuicoes");
                                        }

                                        if (i % 100 == 0)
                                        {
                                            CommitTransaction();
                                            BeginTransaction(AuthenticationContext<SinacorAuthenticationData>.Current.AuthenticationData.CompanyId);
                                        }
                                    }

                                    if (!IsAborted)
                                    {
                                        CommitTransaction();

                                        brokers = raImportacao.ConsultarBrokersImportados(new ProcessEntity.Parameter.FiltroBrokersImportados
                                        {
                                            TipoDistribuicao = entity.TipoDistribuicao.Value
                                        });

                                        if (ImportacaoConcluida != null)
                                        {
                                            filtroRetorno = new ProcessEntity.Parameter.FiltroRetornoImportacaoDistribuicao();
                                            filtroRetorno.Brokers = brokers;
                                            filtroRetorno.CalcularAcrescimoDecrescimo = entity.CalcularAcrescimoDecrescimo;
                                            filtroRetorno.DataMovimento = informacaoMovimento.DataMovimento;
                                            filtroRetorno.Header = _header;
                                            filtroRetorno.NomeArquivo = entity.NomeArquivo;
                                            filtroRetorno.TipoDistribuicao = entity.TipoDistribuicao;
                                            filtroRetorno.TipoDistribuicaoPrecoMedio = entity.TipoDistribuicaoPrecoMedio;
                                            filtroRetorno.TipoFormatoArquivo = entity.TipoFormatoArquivo;

                                            ImportacaoConcluida(this, new ProcessEventArgs<ProcessEntity.Parameter.FiltroRetornoImportacaoDistribuicao>(filtroRetorno));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                RollbackTransaction();

                throw;
            }
            finally
            {
                FecharClient();
            }
        }

        private ProcessEntity.DistribuicaoDetail[] TraduzirDistribuicaoXML(List<Layout.DistribuicaoXMLDistribuicao> distribuicao, object sender)
        {
            List<ProcessEntity.DistribuicaoDetail> retorno = new List<ProcessEntity.DistribuicaoDetail>();

            try
            {
                ProcessEntity.DistribuicaoDetail itemAdicionar;

                foreach (Layout.DistribuicaoXMLDistribuicao item in distribuicao)
                {
                    itemAdicionar = _translatorDistribuicao.ToDistribuicaoDetail(item);

                    if (_header.TipoDistribuicao == ProcessEntity.Enum.TipoDistribuicao.Percentual)
                    {
                        itemAdicionar.Quantidade = Convert.ToDecimal(item.QuantidadePercentual);
                    }
                    else
                    {
                        itemAdicionar.Quantidade = item.Quantidade.Value;
                    }
                }
            }
            catch (Exception ex)
            {
                _client_RecordReadError(sender, new RecordReadErrorEventArgs(_filePath, ex));
            }

            return retorno.ToArray();
        }

        #endregion

        #region Métodos Privados

        private void LerArquivoNegocio(ProcessEntity.Parameter.FiltroImportacaoDistribuicao entity)
        {
            _importFinishControl = new ManualResetEvent(false);

            if (entity.TipoFormatoArquivo == ProcessEntity.Enum.TipoFormatoArquivo.TXT)
            {
                _client = new FlatFileInputClient();

                _client.Open("ImportacaoTORDIST");
            }
            else
            {
                _client = new XmlFileInputClient();
                _client.Open("ImportacaoTORDISTXML");
            }

            _client.FileOpened += new EventHandler<FileOpenedEventArgs>(_client_FileOpened);
            _client.FileClosed += new EventHandler<FileClosedEventArgs>(_client_FileClosed);
            _client.RecordRead += new EventHandler<RecordEventArgs>(_client_RecordRead);
            _client.RecordReadError += new EventHandler<RecordReadErrorEventArgs>(_client_RecordReadError);
            _client.FileCanceled += new EventHandler<FileCanceledEventArgs>(_client_FileCanceled);

            _client.BeginImport(new FileReceiveParameter(System.IO.Path.GetFileName(entity.NomeArquivo)));

            _importFinishControl.WaitOne();
        }

        private void FecharClient()
        {
            if (_client != null)
            {
                if (_client.Status == PSE.Framework.Communication.CommunicationStatus.Opened)
                {
                    _client.EndImport();
                    _client.Close();
                }

                _client = null;
            }

            if (_importFinishControl != null)
                _importFinishControl.Set();
        }

        private void AtualizaStatus(decimal progress, string resourceKey)
        {
            this.Status.Progress = Convert.ToInt32(Math.Round(progress, 0, MidpointRounding.ToEven));
            this.Status.Step = resourceKey;
            this.SendStatus();
        }

        #endregion
    }
}
